Box plots
Box plots are designed to compare the differences of a categorical
variable (samples or groups). They do this by displaying the summary
statistics of a continuous variable (e.g. numeric) for each categorical
variable.
The summary statistics shown are:
- The median (middle value)
- Interquartile range, known as IQR, which has values from 25% to 75%
(or 25th to 75th percentile)
- First quartile, known as Q1, which has a value of 25%
- Second quartile, known as Q3, which has a value of 75%
- “minimum” value, calculated as
Q1 - 1.5*IQR
- “maximum” value, calculated as
Q3 + 1.5*IQR
- Outlier, which are values that fall outside of the maximum or
minimum values
We will use data from the Pokémon games again for our examples for
box plots, which was web scraped from https://pokemondb.net/pokedex/all.
# load and clean names
pokemon <- read_csv("https://raw.githubusercontent.com/andrewmoles2/webScraping/main/R/data/pokemon.csv") %>%
clean_names()
# review data
pokemon %>%
glimpse()
## Rows: 966
## Columns: 14
## $ number <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, …
## $ name <chr> "Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmele…
## $ type1 <chr> "Grass", "Grass", "Grass", "Fire", "Fire", "Fire", "Water",…
## $ type2 <chr> "Poison", "Poison", "Poison", NA, NA, "Flying", NA, NA, NA,…
## $ total <dbl> 318, 405, 525, 309, 405, 534, 314, 405, 530, 195, 205, 395,…
## $ hp <dbl> 45, 60, 80, 39, 58, 78, 44, 59, 79, 45, 50, 60, 40, 45, 65,…
## $ attack <dbl> 49, 62, 82, 52, 64, 84, 48, 63, 83, 30, 20, 45, 35, 25, 90,…
## $ defense <dbl> 49, 63, 83, 43, 58, 78, 65, 80, 100, 35, 55, 50, 30, 50, 40…
## $ sp_atk <dbl> 65, 80, 100, 60, 80, 109, 50, 65, 85, 20, 25, 90, 20, 25, 4…
## $ sp_def <dbl> 65, 80, 100, 50, 65, 85, 64, 80, 105, 20, 25, 80, 20, 25, 8…
## $ speed <dbl> 45, 60, 80, 65, 80, 100, 43, 58, 78, 45, 30, 70, 50, 35, 75…
## $ legendary <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FAL…
## $ generation <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ image_url <chr> "https://img.pokemondb.net/sprites/home/normal/bulbasaur.pn…
For these examples we will just look at one type of Pokémon, the
electric type; the most famous of which is Pikachu! First, we extract
just the electric type Pokémon, and make relevant columns factors.
# select columns to convert to factor
to_factor <- c("type1", "type2", "generation")
# extract just electric pokemon and make cols factors
electric_pokemon <- pokemon %>%
filter(type1 == "Electric" | type2 == "Electric") %>%
mutate(across(all_of(to_factor), factor))
head(electric_pokemon)
## # A tibble: 6 × 14
## number name type1 type2 total hp attack defense sp_atk sp_def speed
## <dbl> <chr> <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 25 Pikachu Electric <NA> 320 35 55 40 50 50 90
## 2 26 Raichu Electric <NA> 485 60 90 55 90 80 110
## 3 81 Magnemite Electric Steel 325 25 35 70 95 55 45
## 4 82 Magneton Electric Steel 465 50 60 95 120 70 70
## 5 100 Voltorb Electric <NA> 330 40 30 50 55 55 100
## 6 101 Electrode Electric <NA> 490 60 50 70 80 80 150
## # ℹ 3 more variables: legendary <lgl>, generation <fct>, image_url <chr>
To make a box plot in ggplot we use the geom_boxplot()
geom function. One of our axis variables has to be categorical and the
other has to be numeric. In the below example we will use generation
(categorical) and total (numeric).
# generation by total
ggplot(electric_pokemon, aes(x = generation, y = total)) +
geom_boxplot()

From the output we see a few things. First is that each box has a
line through the middle which indicates the median; the box itself is
our interquartile range. The lines above and below the boxes (known as
whiskers) are the maximum and minimum values. The black dots indicate
outliers, which have fallen outside our max and min values.
Just like with scatter and bar plots we can change the colours! You
can use either fill or colour arguments with box plots, but fill tends
to look better.
We will use the colour of Pikachu to colour our boxes. We used the
pokemon colour picker to get the colour of pikachu: https://pokepalettes.com/#pikachu
ggplot(electric_pokemon, aes(x = generation, y = total)) +
geom_boxplot(fill = "#f6e652")

Sometimes it is useful to remove the outliers. To do so you add in
the outlier.shape = NA argument.
ggplot(electric_pokemon, aes(x = generation, y = total)) +
geom_boxplot(fill = "#f6e652", outlier.shape = NA)

Displaying outliers is usually a good idea so we will keep them for
now, and change the colour and shape of them. To adjust these we use
outlier.colour and outlier.shape argments.
We’ve used the colour of Pikachu’s cheeks as the outlier colour and made
the shape square.
ggplot(electric_pokemon, aes(x = generation, y = total)) +
geom_boxplot(fill = "#f6e652", outlier.colour = "#c52018",
outlier.shape = 15)

Box plots exercise
For the exercises for this workshops we will be using daily COVID
data that is collected from most of the countries around the world.
COVID data is from our world in data, which is stored in a GitHub
repository. More information on the data and what each variable means
can be found here: https://github.com/owid/covid-19-data/tree/master/public/data
# load in covid data and select cases, deaths and vaccines
covid <- read_csv("https://covid.ourworldindata.org/data/owid-covid-data.csv") %>%
select(iso_code:new_deaths_smoothed_per_million, contains("vaccin"),
population, median_age, gdp_per_capita)
## Rows: 346487 Columns: 67
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (4): iso_code, continent, location, tests_units
## dbl (62): total_cases, new_cases, new_cases_smoothed, total_deaths, new_dea...
## date (1): date
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# have a quick look at the data
covid %>% glimpse()
## Rows: 346,487
## Columns: 30
## $ iso_code <chr> "AFG", "AFG", "AFG", "AFG",…
## $ continent <chr> "Asia", "Asia", "Asia", "As…
## $ location <chr> "Afghanistan", "Afghanistan…
## $ date <date> 2020-01-03, 2020-01-04, 20…
## $ total_cases <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_cases <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ new_cases_smoothed <dbl> NA, NA, NA, NA, NA, 0, 0, 0…
## $ total_deaths <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ new_deaths_smoothed <dbl> NA, NA, NA, NA, NA, 0, 0, 0…
## $ total_cases_per_million <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_cases_per_million <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ new_cases_smoothed_per_million <dbl> NA, NA, NA, NA, NA, 0, 0, 0…
## $ total_deaths_per_million <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_deaths_per_million <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ new_deaths_smoothed_per_million <dbl> NA, NA, NA, NA, NA, 0, 0, 0…
## $ total_vaccinations <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_vaccinated <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_fully_vaccinated <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations_smoothed <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ total_vaccinations_per_hundred <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_vaccinated_per_hundred <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ people_fully_vaccinated_per_hundred <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_vaccinations_smoothed_per_million <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_people_vaccinated_smoothed <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ new_people_vaccinated_smoothed_per_hundred <dbl> NA, NA, NA, NA, NA, NA, NA,…
## $ population <dbl> 41128772, 41128772, 4112877…
## $ median_age <dbl> 18.6, 18.6, 18.6, 18.6, 18.…
## $ gdp_per_capita <dbl> 1803.987, 1803.987, 1803.98…
For this exercise will we make two box plots from our data looking
more at the demographics of each continent (we will look at cases and
vaccines later).
Your two box plots should show the following:
- The median age of each continent
- The gdp per capita for each continent
- Make sure to change the colour of the boxes and outliers to make it
look better!
- Try changing the shape and size of your outlier
Hint: you will have to remove the na values from continent before
plotting, e.g. covid %>% filter(!is.na(continent))
Hint: You can pipe from your filter function straight into
ggplot2!
Hint: You can add colours in lots of ways but it can be fun to use a
colour picker http://tristen.ca/hcl-picker/#/hlc/11/1.1/DC7261/D77357.
Displaying distributions with histograms
Histograms are great for visualising the distribution of numeric
data. Histograms have one numerical variable as their input.
To make a histogram with ggplot we provide a numerical value to our x
axis, and use the geom_histogram() geom. In the example we
are using all the pokemon data and showing the distribution of the total
column.
ggplot(pokemon, aes(x = total)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

We can adjust the size of the bins of our plot with two
methods, changing the binwidth or selecting the amount of bins. When we
talk about bins with histograms it refers to the size of each bar; the
larger the bar the more data on the x axis is included.
The first example uses binwidth. The number you provide
is directly related to your x axis. In our example we are using the
total column which goes up to 754. If we have binwidth = 8,
then 8 data points will be included in each bin. Run the two examples
below with a smaller and larger binwidth to see the results.
# summary stats for total column
summary(pokemon$total)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 175.0 328.0 453.0 430.6 506.5 754.0
# binwidth of 8
ggplot(pokemon, aes(x = total)) +
geom_histogram(binwidth = 8) +
labs(title = "Small binwidth (8)")

# binwidth of 50
ggplot(pokemon, aes(x = total)) +
geom_histogram(binwidth = 50) +
labs(title = "Larger binwidth (50)")

The other method is to select the number of bins to use, using the
bins argument. The more bins we use, the less data will be
contained in each bin. In the example below we have bins with lots of
data bins = 10 and bins with less data
bins = 50. Which do you think is best?
# using 10 bins
ggplot(pokemon, aes(x = total)) +
geom_histogram(bins = 10) +
labs(title = "Less bins = more data in each bin")

# using 50 bins
ggplot(pokemon, aes(x = total)) +
geom_histogram(bins = 50) +
labs(title = "More bins = less data in each bin")

It can be helpful to colour your histogram by a categorical variable.
This works the same as a box plot, using the fill argument.
In the example we have filled our histogram by the legendary
category.
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20)

Another useful method is to use facets, which split up your
data by a categorical variable and presents them in a grid like
formation.
There are two techniques in ggplot to make facets, using
facet_grid() or facet_wrap(). To use
facet_grid() we define if we want to display our data
row-wise (rows =) or column-wise (cols =).
When defining which column to split our data by we need to use the
vars() function. See the two examples below on how to do a
row or column facet grid.
# row-wise display
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20) +
facet_grid(rows = vars(legendary)) +
labs(title = "Row-wise facet grid")

# column-wise display
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20) +
facet_grid(cols = vars(legendary)) +
labs(title = "column-wise facet grid")

The other option is facet_wrap(), which by default only
needs the column you want to split your data by. It does allow extra
specification with the nrow and ncol
functions, allowing you to define how many rows and columns to
display.
In the examples below we show the default facet_wrap,
and how to adjust the column or row specification. We have used the
generation column as it has more groups.
# default facet_wrap
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20) +
facet_wrap(vars(generation)) +
labs(title = "Default facet wrap")

# 4 rows
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20) +
facet_wrap(vars(generation),
nrow = 4) +
labs(title = "Facet wrap with 4 rows")

# 4 columns
ggplot(pokemon, aes(x = total, fill = legendary)) +
geom_histogram(binwidth = 20) +
facet_wrap(vars(generation),
ncol = 4) +
labs(title = "Facet wrap with 4 columns")

Displaying distributions exercise
For this exercise we will be making a histogram of using the
people_fully_vaccinated_per_hundred column for each continent
- Make a histogram with people_fully_vaccinated_per_hundred as your x
axis
- Add a fill argment with continent
- Adjust the
binwidth or bins
(e.g. binwidth = 5 looks good)
- Using RColourBrewer, adjust the colours used in fill
Hint: you will have to remove the na values from continent before
plotting, e.g. covid %>% filter(!is.na(continent))
Hint: You can pipe from your filter function straight into
ggplot2!
Hint: To change the fill colours you can use
scale_fill_brewer(palette = "a palette")
Hint: Use brewer.pal.info to find RColorBrewer
palettes
Working with the date data type with lubridate
Working with the date data type when programming can be a bit tricky
for many reasons. There are different formats, time zones, and the
challenge extracting information from the date. Fortunately, the
lubridate package comes to the rescue!
There are three types of date data type: date (2010-09-01), time
(15:08:52 BST), date-time (2010-09-01 15:08:52 BST). For this workshop
we will be focusing on the date type as it is the most common.
You can find out today’s date (more useful than it sounds) or the
date and time using the today() or now()
functions.
# make sure dplyr and lubridate are loaded
library(dplyr)
library(lubridate)
# get today's date
today()
## [1] "2023-10-05"
# today's date and time
now()
## [1] "2023-10-05 15:16:56 BST"
# make today's date a variable
today_date <- today()
A great feature of lubridate is extracting the year, month, day, or
week day information from your date. We can test it out on today’s date.
Run the code to see how the output.
## [1] 2023
# month
month(today_date)
## [1] 10
month(today_date, label = TRUE)
## [1] Oct
## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec
## [1] 40
## [1] 5
# weekday
wday(today_date)
## [1] 5
wday(today_date, label = TRUE)
## [1] Thu
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat
Notice that for the month and wday
functions we have the option to add labels. This can be very useful,
making your month or week day outputs more readable.
For the rest of the examples we will use some randomised made up data
containing daily sleep, and step information. Run the code below to see
the data.
note: to make this data we have used randomisation functions:
sample, runif and rnorm, if you
are interested look them up to see how they work
# make some random data
df <- data.frame(
date = seq(as.Date("2019-01-01"), as.Date("2021-12-01"), by = "days"),
hours_sleep = round(rnorm(1066, mean = 9, sd = 1.5)),
steps = round(rnorm(1066, mean = 8000, sd = 2000))
)
head(df)
## date hours_sleep steps
## 1 2019-01-01 12 8725
## 2 2019-01-02 9 6942
## 3 2019-01-03 7 9210
## 4 2019-01-04 10 4689
## 5 2019-01-05 6 8487
## 6 2019-01-06 9 12253
We can now use the mutate function to make a year,
month, week, day, and week day column.
df <- df %>%
mutate(year = year(date),
month = month(date, label = TRUE),
week = week(date),
day = day(date),
week_day = wday(date, label = TRUE))
head(df)
## date hours_sleep steps year month week day week_day
## 1 2019-01-01 12 8725 2019 Jan 1 1 Tue
## 2 2019-01-02 9 6942 2019 Jan 1 2 Wed
## 3 2019-01-03 7 9210 2019 Jan 1 3 Thu
## 4 2019-01-04 10 4689 2019 Jan 1 4 Fri
## 5 2019-01-05 6 8487 2019 Jan 1 5 Sat
## 6 2019-01-06 9 12253 2019 Jan 1 6 Sun
# see the breakdown of the date
df[1:2, c("date", "year", "month", "week", "day", "week_day")]
## date year month week day week_day
## 1 2019-01-01 2019 Jan 1 1 Tue
## 2 2019-01-02 2019 Jan 1 2 Wed
Breaking the date down in this way allows us to do some aggregation
of our data by the year, month, week, day, or weekday! In the examples
below we have shown year and weekday.
# aggregate by year
df %>%
group_by(year) %>%
summarise(avg_sleep = mean(hours_sleep),
avg_steps = mean(steps),
total_steps = sum(steps))
## # A tibble: 3 × 4
## year avg_sleep avg_steps total_steps
## <dbl> <dbl> <dbl> <dbl>
## 1 2019 9.04 8123. 2964736
## 2 2020 9.04 8090. 2960777
## 3 2021 8.99 8119. 2719914
# aggregate by week day
df %>%
group_by(week_day) %>%
summarise(avg_sleep = mean(hours_sleep),
avg_steps = mean(steps),
total_steps = sum(steps))
## # A tibble: 7 × 4
## week_day avg_sleep avg_steps total_steps
## <ord> <dbl> <dbl> <dbl>
## 1 Sun 9.20 8139. 1237159
## 2 Mon 8.92 7952. 1208753
## 3 Tue 9.18 8250. 1262292
## 4 Wed 8.97 7890. 1207152
## 5 Thu 8.92 8268. 1256665
## 6 Fri 9.10 8098. 1230831
## 7 Sat 8.88 8175. 1242575
There are more functions from the lubridate package that we won’t be
able to cover in this session, so do have a look at the package website
for more information - https://lubridate.tidyverse.org/index.html - and
checkout the R for Data Science chapter on dates - https://r4ds.had.co.nz/dates-and-times.html.
lubridate exercise
Using the examples above, extract year, month, day, day of week from
covid data, and do an aggregation!
- Add new columns to your covid data for year, month, week, day and
week_day. Try to add labels to month and week_day.
- Aggregate your covid data by year and month to find the mean total
cases per million and mean total deaths per million.
- Print out the result.
# your code here
# separate date column
# make year and month aggregate
Time series plots
Time series plots visualise data over a period of time, which can be
hourly, daily, weekly, monthly, or yearly. It is a great way to view
trends over time. When plotting a time series, the x axis is the date
and the y axis is your measure.
The most simple form of a time series visualisation in R is to use an
unedited date variable. Using our example data (df) we will
visualise how steps have changed each day.
# daily time series
df %>%
ggplot(aes(x = date, y = steps)) +
geom_line()

As we can see it is pretty variable how many steps are taken each
day, as you might expect. There is a lot of data here so it is hard to
see any real patterns, it just looks like noise! To solve this we can
aggregate our data by the year, the month or the week to see if we can
get any more insights.
For the example data we have it might be interesting to see the
average of how many steps are taken on average each month, and to also
compare this year on year.
We first aggregate our data, grouping by the month and year columns
we made with the lubridate package, find the average steps, and convert
the year column into a factor to make plotting easier; month is already
a factor.
# aggregated time series by month
monthly_steps <- df %>%
group_by(month, year) %>%
summarise(avg_steps = mean(steps)) %>%
mutate(year = factor(year))
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
## # A tibble: 36 × 3
## # Groups: month [12]
## month year avg_steps
## <ord> <fct> <dbl>
## 1 Jan 2019 8042.
## 2 Jan 2020 8224.
## 3 Jan 2021 7962.
## 4 Feb 2019 7556.
## 5 Feb 2020 8810.
## 6 Feb 2021 7745.
## 7 Mar 2019 8828.
## 8 Mar 2020 7469.
## 9 Mar 2021 8330.
## 10 Apr 2019 7865.
## # ℹ 26 more rows
Now we can make a time series by month! It is often helpful when
using geom_line() to also pair it with
geom_point() so we can see each data point clearly as well
as seeing the trends with shown by the lines.
ggplot(monthly_steps,
aes(x = month, y = avg_steps)) +
geom_line() +
geom_point()

That didn’t work as expected! As our data is grouped by year and
month we need to use the group = argument to tell ggplot we
want to connect the months up.
By adding group = year our plot will now look like a
time series, run the code to check it out.
ggplot(monthly_steps,
aes(x = month, y = avg_steps,
group = year)) +
geom_line() +
geom_point()

It would also be helpful to see what year each line represents. We
add the colour = year argument in as well to show this.
ggplot(monthly_steps,
aes(x = month, y = avg_steps,
group = year, colour = year)) +
geom_line() +
geom_point()

Our plot is still looking a little busy so we can use facets to split
our data by year. We’ve used facet_wrap here with 3
rows.
ggplot(monthly_steps,
aes(x = month, y = avg_steps,
group = year, colour = year)) +
geom_line() +
geom_point() +
facet_wrap(vars(year), nrow = 3)

Finally, we can make a few final adjustments and we have a nice
visualisation that shows average step count per month for the year 2019
to 2021. Below is a list of all the additions make to change the look of
the plot:
- Changed the size of the lines and the points with the
size = argument
- Added a title and changed the axis names
- Added a colour scale from the RColorBrewer package
- Changed the theme to dark and changed the font to Avenir
- Adjusted the y axis limits
step_count <- ggplot(monthly_steps,
aes(x = month, y = avg_steps,
group = year, colour = year)) +
geom_line(size = 2.5) +
geom_point(size = 3) +
facet_wrap(vars(year), nrow = 3) +
labs(title = "Average step count per month for the year 2019 to 2021",
x = "Month", y = "Average steps (mean)",
colour = "Year") +
scale_colour_brewer(palette = "Pastel2") +
theme_dark(base_family = "Avenir") +
scale_y_continuous(limits = c(7000, 9000))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: Removed 1 row containing missing values (`geom_line()`).
## Warning: Removed 1 rows containing missing values (`geom_point()`).

ggsave("step_count.png", step_count, width = 9)
## Saving 9 x 5 in image
## Warning: Removed 1 row containing missing values (`geom_line()`).
## Removed 1 rows containing missing values (`geom_point()`).
Time series plots exercise
For this exercise we will be looking at the vaccine roll out for
United Kingdom, India, Nepal, Israel, Germany, and Australia. Each
country has had slightly different roll outs, with Israel being the
fastest. We will be looking at the week by week roll out for 2021.
Data preparation:
- Make a vector called sel_country that includes United
Kingdom, India, Nepal, Israel, Germany, and Australia
- Filter your covid data to include only locations that are in your
sel_country vector, and filter for the year to be equal to 2021. Assign
your filtered data to a variable called weekly_vax.
- Aggregate your weekly_vax data by week and location to find
the mean of the
people_vaccinated_per_hundred column.
Assign the result back to weekly_vax
- Make the week and location columns of weekly_vax
factors
Plotting:
Using your weekly_vax data you have just prepared:
- Make a time series plot with week as your x axis and your
aggregation of the
people_vaccinated_per_hundred column as
your y axis.
- Colour and group your data by location.
- Make any aesthetic changes you think will make the plot better based
on what we have covered so far, such as adding titles, changing colours,
or adding facets (
facet_grid() or
facet_wrap()).
- Assign your final plot to a variable and save it!
Hint: if your x axis is looking squashed or cramped, try adding in
scale_x_discrete(guide = guide_axis(n.dodge = 2))
LS0tCnRpdGxlOiAiUiBEYXRhIFZpc3VhbGlzdGlvbiAyOiBCb3gsIGhpc3RvZ3JhbSwgYW5kIGxpbmUgcGxvdHMiCmF1dGhvcjoKICAgLSBuYW1lOiBBbmRyZXcgTW9sZXMKICAgICBhZmZpbGlhdGlvbjogTGVhcm5pbmcgRGV2ZWxvcGVyLCBEaWdpdGFsIFNraWxscyBMYWIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICBrZWVwX21kOiBubwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgojIE9iamVjdGl2ZSBvZiB3b3Jrc2hvcAoKVG8gY3JlYXRlIGhpc3RvZ3JhbXMsIGJveCwgYW5kIHRpbWUgc2VyaWVzIHBsb3RzIHVzaW5nIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuIAoKIyBXaGF0IHRoaXMgd29ya3Nob3Agd2lsbCBjb3ZlcgoKSW4gdGhpcyB3b3Jrc2hvcCwgdGhlIGFpbSBpcyB0byBjb3ZlciBob3cgdG8gd29yayB3aXRoIGRhdGVzIGluIHBsb3RzLCBhbmQgdXNlIGhpc3RvZ3JhbXMgYW5kIGJveCBwbG90cy4gV2Ugd2lsbCBiZSBjb3ZlcmluZzogCgotICAgSG93IHRvIG1ha2UgYm94IHBsb3RzIHdpdGggZ2dwbG90MgotICAgRGlzcGxheWluZyBkaXN0cmlidXRpb25zIHdpdGggaGlzdG9ncmFtcwotICAgV29ya2luZyB3aXRoIGRhdGVzIHdpdGggdGhlIGx1YnJpZGF0ZSBwYWNrYWdlCi0gICBIb3cgdG8gbWFrZSB0aW1lIHNlcmllcyBsaW5lIHBsb3RzCi0gICBIb3cgdG8gc3BsaXQgeW91ciBkYXRhIGludG8gZmFjZXQgZ3JpZHMKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKSW4gdGhpcyBkYXRhIHZpc3VhbGlzYXRpb24gd29ya3Nob3Agd2Ugd2lsbCBiZSBidWlsZGluZyBvbiB0aGUgY29uY2VwdHMgbGVhcm50IGluIHRoZSBmaXJzdCB3b3Jrc2hvcCwgY29uc3RydWN0aW5nIHZpc3VhbGlzYXRpb25zIHVzaW5nIHRoZSBgZ2dwbG90MmAgbGlicmFyeS4gCgohW10oaHR0cHM6Ly9naXRodWIuY29tL2FuZHJld21vbGVzMi9yVHJhaW5JbnRyb2R1Y3Rpb24vYmxvYi9tYWluL3ItZGF0YS12aXN1YWxpc2F0aW9uLTEvaW1hZ2VzL2dncGxvdDJfbWFzdGVycGllY2UucG5nP3Jhdz10cnVlKXt3aWR0aD0iNTQxIn0KCldlIHdpbGwgYmUgdXNpbmcgb25lIG5ldyBwYWNrYWdlIGNhbGxlZCAqbHVicmlkYXRlKiwgYSB0aWR5dmVyc2UgcGFja2FnZSB3aGljaCBpcyBkZXNpZ25lZCB0byBtYWtlIHdvcmtpbmcgd2l0aCBkYXRlcyBhbmQgdGltZXMgZWFzaWVyOyB0aGlzIHdpbGwgaGVscCB1cyBpbiBtYWtpbmcgdGltZSBzZXJpZXMgdmlzdWFsaXNhdGlvbnMuICoqUnVuIHRoZSB0aGUgY29kZSBiZWxvdyB0byBpbnN0YWxsIGx1YnJpZGF0ZSoqLiAKCmBgYHtyIGV2YWw9RkFMU0V9CiMgaW5zdGFsbCBsdWJyaWRhdGUKaW5zdGFsbC5wYWNrYWdlcygibHVicmlkYXRlIikKYGBgCgpCZWZvcmUgd2Ugc3RhcnQgd2Ugd2lsbCBuZWVkIHRvIGxvYWQgdGhlIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nIGR1cmluZyB0aGlzIHNlc3Npb24uICoqUnVuIHRoZSBjb2RlIGJlbG93IHRvIGxvYWQgeW91ciBsaWJyYXJpZXMqKi4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyBCb3ggcGxvdHMKCkJveCBwbG90cyBhcmUgZGVzaWduZWQgdG8gY29tcGFyZSB0aGUgZGlmZmVyZW5jZXMgb2YgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoc2FtcGxlcyBvciBncm91cHMpLiBUaGV5IGRvIHRoaXMgYnkgZGlzcGxheWluZyB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIG9mIGEgY29udGludW91cyB2YXJpYWJsZSAoZS5nLiBudW1lcmljKSBmb3IgZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gCgpUaGUgc3VtbWFyeSBzdGF0aXN0aWNzIHNob3duIGFyZTogCgotIFRoZSBtZWRpYW4gKG1pZGRsZSB2YWx1ZSkKLSBJbnRlcnF1YXJ0aWxlIHJhbmdlLCBrbm93biBhcyBJUVIsIHdoaWNoIGhhcyB2YWx1ZXMgZnJvbSAyNSUgdG8gNzUlIChvciAyNXRoIHRvIDc1dGggcGVyY2VudGlsZSkKLSBGaXJzdCBxdWFydGlsZSwga25vd24gYXMgUTEsIHdoaWNoIGhhcyBhIHZhbHVlIG9mIDI1JQotIFNlY29uZCBxdWFydGlsZSwga25vd24gYXMgUTMsIHdoaWNoIGhhcyBhIHZhbHVlIG9mIDc1JQotICJtaW5pbXVtIiB2YWx1ZSwgY2FsY3VsYXRlZCBhcyBgUTEgLSAxLjUqSVFSYAotICJtYXhpbXVtIiB2YWx1ZSwgY2FsY3VsYXRlZCBhcyBgUTMgKyAxLjUqSVFSYAotIE91dGxpZXIsIHdoaWNoIGFyZSB2YWx1ZXMgdGhhdCBmYWxsIG91dHNpZGUgb2YgdGhlIG1heGltdW0gb3IgbWluaW11bSB2YWx1ZXMKCldlIHdpbGwgdXNlIGRhdGEgZnJvbSB0aGUgUG9rw6ltb24gZ2FtZXMgYWdhaW4gZm9yIG91ciBleGFtcGxlcyBmb3IgYm94IHBsb3RzLCB3aGljaCB3YXMgd2ViIHNjcmFwZWQgZnJvbSA8aHR0cHM6Ly9wb2tlbW9uZGIubmV0L3Bva2VkZXgvYWxsPi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMgbG9hZCBhbmQgY2xlYW4gbmFtZXMKcG9rZW1vbiA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2FuZHJld21vbGVzMi93ZWJTY3JhcGluZy9tYWluL1IvZGF0YS9wb2tlbW9uLmNzdiIpICU+JQogIGNsZWFuX25hbWVzKCkKIyByZXZpZXcgZGF0YQpwb2tlbW9uICU+JQogIGdsaW1wc2UoKQpgYGAKCkZvciB0aGVzZSBleGFtcGxlcyB3ZSB3aWxsIGp1c3QgbG9vayBhdCBvbmUgdHlwZSBvZiBQb2vDqW1vbiwgdGhlIGVsZWN0cmljIHR5cGU7IHRoZSBtb3N0IGZhbW91cyBvZiB3aGljaCBpcyBQaWthY2h1ISBGaXJzdCwgd2UgZXh0cmFjdCBqdXN0IHRoZSBlbGVjdHJpYyB0eXBlIFBva8OpbW9uLCBhbmQgbWFrZSByZWxldmFudCBjb2x1bW5zIGZhY3RvcnMuIAoKYGBge3J9CiMgc2VsZWN0IGNvbHVtbnMgdG8gY29udmVydCB0byBmYWN0b3IKdG9fZmFjdG9yIDwtIGMoInR5cGUxIiwgInR5cGUyIiwgImdlbmVyYXRpb24iKQoKIyBleHRyYWN0IGp1c3QgZWxlY3RyaWMgcG9rZW1vbiBhbmQgbWFrZSBjb2xzIGZhY3RvcnMKZWxlY3RyaWNfcG9rZW1vbiA8LSBwb2tlbW9uICU+JQogIGZpbHRlcih0eXBlMSA9PSAiRWxlY3RyaWMiIHwgdHlwZTIgPT0gIkVsZWN0cmljIikgJT4lCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YodG9fZmFjdG9yKSwgZmFjdG9yKSkKCmhlYWQoZWxlY3RyaWNfcG9rZW1vbikKYGBgCgpUbyBtYWtlIGEgYm94IHBsb3QgaW4gZ2dwbG90IHdlIHVzZSB0aGUgYGdlb21fYm94cGxvdCgpYCBnZW9tIGZ1bmN0aW9uLiBPbmUgb2Ygb3VyIGF4aXMgdmFyaWFibGVzIGhhcyB0byBiZSBjYXRlZ29yaWNhbCBhbmQgdGhlIG90aGVyIGhhcyB0byBiZSBudW1lcmljLiBJbiB0aGUgYmVsb3cgZXhhbXBsZSB3ZSB3aWxsIHVzZSBnZW5lcmF0aW9uIChjYXRlZ29yaWNhbCkgYW5kIHRvdGFsIChudW1lcmljKS4gCgpgYGB7cn0KIyBnZW5lcmF0aW9uIGJ5IHRvdGFsCmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoKQpgYGAKCkZyb20gdGhlIG91dHB1dCB3ZSBzZWUgYSBmZXcgdGhpbmdzLiBGaXJzdCBpcyB0aGF0IGVhY2ggYm94IGhhcyBhIGxpbmUgdGhyb3VnaCB0aGUgbWlkZGxlIHdoaWNoIGluZGljYXRlcyB0aGUgbWVkaWFuOyB0aGUgYm94IGl0c2VsZiBpcyBvdXIgaW50ZXJxdWFydGlsZSByYW5nZS4gVGhlIGxpbmVzIGFib3ZlIGFuZCBiZWxvdyB0aGUgYm94ZXMgKGtub3duIGFzIHdoaXNrZXJzKSBhcmUgdGhlIG1heGltdW0gYW5kIG1pbmltdW0gdmFsdWVzLiBUaGUgYmxhY2sgZG90cyBpbmRpY2F0ZSBvdXRsaWVycywgd2hpY2ggaGF2ZSBmYWxsZW4gb3V0c2lkZSBvdXIgbWF4IGFuZCBtaW4gdmFsdWVzLiAKCkp1c3QgbGlrZSB3aXRoIHNjYXR0ZXIgYW5kIGJhciBwbG90cyB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvdXJzISBZb3UgY2FuIHVzZSBlaXRoZXIgZmlsbCBvciBjb2xvdXIgYXJndW1lbnRzIHdpdGggYm94IHBsb3RzLCBidXQgZmlsbCB0ZW5kcyB0byBsb29rIGJldHRlci4gCgpXZSB3aWxsIHVzZSB0aGUgY29sb3VyIG9mIFBpa2FjaHUgdG8gY29sb3VyIG91ciBib3hlcy4gV2UgdXNlZCB0aGUgcG9rZW1vbiBjb2xvdXIgcGlja2VyIHRvIGdldCB0aGUgY29sb3VyIG9mIHBpa2FjaHU6IDxodHRwczovL3Bva2VwYWxldHRlcy5jb20vI3Bpa2FjaHU+CgpgYGB7cn0KZ2dwbG90KGVsZWN0cmljX3Bva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IHRvdGFsKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gIiNmNmU2NTIiKQpgYGAKClNvbWV0aW1lcyBpdCBpcyB1c2VmdWwgdG8gcmVtb3ZlIHRoZSBvdXRsaWVycy4gVG8gZG8gc28geW91IGFkZCBpbiB0aGUgYG91dGxpZXIuc2hhcGUgPSBOQWAgYXJndW1lbnQuIAoKYGBge3J9CmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjZjZlNjUyIiwgb3V0bGllci5zaGFwZSA9IE5BKQpgYGAKCkRpc3BsYXlpbmcgb3V0bGllcnMgaXMgdXN1YWxseSBhIGdvb2QgaWRlYSBzbyB3ZSB3aWxsIGtlZXAgdGhlbSBmb3Igbm93LCBhbmQgY2hhbmdlIHRoZSBjb2xvdXIgYW5kIHNoYXBlIG9mIHRoZW0uIFRvIGFkanVzdCB0aGVzZSB3ZSB1c2UgYG91dGxpZXIuY29sb3VyYCBhbmQgYG91dGxpZXIuc2hhcGVgIGFyZ21lbnRzLiBXZSd2ZSB1c2VkIHRoZSBjb2xvdXIgb2YgUGlrYWNodSdzIGNoZWVrcyBhcyB0aGUgb3V0bGllciBjb2xvdXIgYW5kIG1hZGUgdGhlIHNoYXBlIHNxdWFyZS4gCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuY29sb3VyID0gIiNjNTIwMTgiLAogICAgICAgICAgICAgICBvdXRsaWVyLnNoYXBlID0gMTUpCmBgYAoKIyMgQm94IHBsb3RzIGV4ZXJjaXNlIAoKRm9yIHRoZSBleGVyY2lzZXMgZm9yIHRoaXMgd29ya3Nob3BzIHdlIHdpbGwgYmUgdXNpbmcgZGFpbHkgQ09WSUQgZGF0YSB0aGF0IGlzIGNvbGxlY3RlZCBmcm9tIG1vc3Qgb2YgdGhlIGNvdW50cmllcyBhcm91bmQgdGhlIHdvcmxkLiAKCkNPVklEIGRhdGEgaXMgZnJvbSBvdXIgd29ybGQgaW4gZGF0YSwgd2hpY2ggaXMgc3RvcmVkIGluIGEgR2l0SHViIHJlcG9zaXRvcnkuIE1vcmUgaW5mb3JtYXRpb24gb24gdGhlIGRhdGEgYW5kIHdoYXQgZWFjaCB2YXJpYWJsZSBtZWFucyBjYW4gYmUgZm91bmQgaGVyZTogPGh0dHBzOi8vZ2l0aHViLmNvbS9vd2lkL2NvdmlkLTE5LWRhdGEvdHJlZS9tYXN0ZXIvcHVibGljL2RhdGE+CgpgYGB7cn0KIyBsb2FkIGluIGNvdmlkIGRhdGEgYW5kIHNlbGVjdCBjYXNlcywgZGVhdGhzIGFuZCB2YWNjaW5lcwpjb3ZpZCA8LSByZWFkX2NzdigiaHR0cHM6Ly9jb3ZpZC5vdXJ3b3JsZGluZGF0YS5vcmcvZGF0YS9vd2lkLWNvdmlkLWRhdGEuY3N2IikgJT4lCiAgc2VsZWN0KGlzb19jb2RlOm5ld19kZWF0aHNfc21vb3RoZWRfcGVyX21pbGxpb24sIGNvbnRhaW5zKCJ2YWNjaW4iKSwKICAgICAgICAgcG9wdWxhdGlvbiwgbWVkaWFuX2FnZSwgZ2RwX3Blcl9jYXBpdGEpCgojIGhhdmUgYSBxdWljayBsb29rIGF0IHRoZSBkYXRhCmNvdmlkICU+JSBnbGltcHNlKCkKYGBgCgpGb3IgdGhpcyBleGVyY2lzZSB3aWxsIHdlIG1ha2UgdHdvIGJveCBwbG90cyBmcm9tIG91ciBkYXRhIGxvb2tpbmcgbW9yZSBhdCB0aGUgZGVtb2dyYXBoaWNzIG9mIGVhY2ggY29udGluZW50ICh3ZSB3aWxsIGxvb2sgYXQgY2FzZXMgYW5kIHZhY2NpbmVzIGxhdGVyKS4KCllvdXIgdHdvIGJveCBwbG90cyBzaG91bGQgc2hvdyB0aGUgZm9sbG93aW5nOgoKLSBUaGUgbWVkaWFuIGFnZSBvZiBlYWNoIGNvbnRpbmVudAotIFRoZSBnZHAgcGVyIGNhcGl0YSBmb3IgZWFjaCBjb250aW5lbnQKLSBNYWtlIHN1cmUgdG8gY2hhbmdlIHRoZSBjb2xvdXIgb2YgdGhlIGJveGVzIGFuZCBvdXRsaWVycyB0byBtYWtlIGl0IGxvb2sgYmV0dGVyISAKLSBUcnkgY2hhbmdpbmcgdGhlIHNoYXBlIGFuZCBzaXplIG9mIHlvdXIgb3V0bGllcgoKSGludDogeW91IHdpbGwgaGF2ZSB0byByZW1vdmUgdGhlIG5hIHZhbHVlcyBmcm9tIGNvbnRpbmVudCBiZWZvcmUgcGxvdHRpbmcsIGUuZy4gYGNvdmlkICU+JSBmaWx0ZXIoIWlzLm5hKGNvbnRpbmVudCkpYAoKSGludDogWW91IGNhbiBwaXBlIGZyb20geW91ciBmaWx0ZXIgZnVuY3Rpb24gc3RyYWlnaHQgaW50byBnZ3Bsb3QyIQoKSGludDogWW91IGNhbiBhZGQgY29sb3VycyBpbiBsb3RzIG9mIHdheXMgYnV0IGl0IGNhbiBiZSBmdW4gdG8gdXNlIGEgY29sb3VyIHBpY2tlciA8aHR0cDovL3RyaXN0ZW4uY2EvaGNsLXBpY2tlci8jL2hsYy8xMS8xLjEvREM3MjYxL0Q3NzM1Nz4uIAoKYGBge3J9CiMgeW91ciBjb2RlIGhlcmUKCgpgYGAKCgojIEltcHJvdmluZyB5b3VyIGJveCBwbG90cwoKVGhlIG1haW4gaXNzdWUgd2l0aCBib3ggcGxvdHMsIGluIGEgc2ltaWxhciB3YXkgdG8gYmFyIHBsb3RzLCBpcyB0aGV5IGNhbiBoaWRlIGRhdGEuIFdlIGNhbiBmaXggdGhpcyBieSBhZGRpbmcgYSBzY2F0dGVyIHBsb3Qgb3ZlciB0aGUgdG9wIG9mIHRoZSBib3hlcyBzbyB3ZSBjYW4gc2VlIHRoZSBmdWxsIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YS4gCgpXaGVuIGFkZGluZyBpbiBhIHNjYXR0ZXIgcGxvdCwgd2Ugd29uJ3QgbmVlZCBvdXIgb3V0bGllcnMgYXMgdGhlIHNjYXR0ZXIgcGxvdCB3aWxsIHNob3cgdGhlc2UgZm9yIHVzLiBXZSB3aWxsIG5lZWQgdG8gcmVtb3ZlIHRoZW0gdXNpbmcgdGhlIGBvdXRsaWVyLnNoYXBlID0gTkFgIGFyZ3VtZW50LiAKCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21fcG9pbnQoKQpgYGAKClNvbWUgb2Ygb3VyIGRhdGEgcG9pbnRzIGFyZSBvdmVybGFwcGluZyB3aGljaCBtYWtlcyBpdCBhIGxpdHRsZSBoYXJkIHRvIHNlZSBhbGwgdGhlIGRhdGEuIFdlIGNhbiBmaXggdGhpcyBieSBjaGFuZ2luZyB0aGUgcG9zaXRpb24gb2Ygb3VyIHBvaW50cyB1c2luZyB0aGUgYHBvc2l0aW9uID0gImppdHRlciJgIGFyZ3VtZW50LiBXZSBjYW4gYWxzbyB1c2UgYGdlb21faml0dGVyKClgIHdoaWNoIGlzIGEgc2hvcnQgaGFuZCBmb3IgYGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIilgOyB3ZSB3aWxsIHVzZSBgZ2VvbV9qaXR0ZXIoKWAgZ29pbmcgZm9yd2FyZCBhcyBpdCBpcyBsZXNzIHR5cGluZy4gCgpgYGB7cn0KIyBjaGFuZ2UgcG9zaXRpb24gaW4gZ2VvbV9wb2ludApnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21fcG9pbnQocG9zaXRpb24gPSAiaml0dGVyIikKCiMgdXNpbmcgZ2VvbV9qaXR0ZXIKZ2dwbG90KGVsZWN0cmljX3Bva2Vtb24sIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IHRvdGFsKSkgKwogIGdlb21fYm94cGxvdChmaWxsID0gIiNmNmU2NTIiLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICBnZW9tX2ppdHRlcigpCmBgYAoKV2UgY2FuIGFsc28gYWRkIGluIGEgY29sb3VyIGdyb3VwaW5nIHRvIG91ciBwb2ludHMgdG8gbWFrZSB0aGVtIG1vcmUgbWVhbmluZ2Z1bC4gV2UgYWRkIHRoZSBjb2xvdXIgYWVzdGhldGljIHRvIG91ciBgZ2VvbV9qaXR0ZXJgIGZ1bmN0aW9uLiBJbiB0aGUgZXhhbXBsZSB3ZSBhcmUgY29sb3VyaW5nIG91ciBwb2ludHMgYnkgaWYgYSBwb2tlbW9uIGlzIGxlZ2VuZGFyeSBvciBub3QuIAoKYGBge3J9CmdncGxvdChlbGVjdHJpY19wb2tlbW9uLCBhZXMoeCA9IGdlbmVyYXRpb24sIHkgPSB0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjZjZlNjUyIiwgb3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91ciA9IGxlZ2VuZGFyeSkpCmBgYAoKRmluYWxseSB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvdXJzIG9mIG91ciBwb2ludHMsIHdoaWNoIGluIHRoaXMgY2FzZSB3ZSBoYXZlIGRvbmUgbWFudWFsbHkuIEFnYWluLCB0aGUgY29sb3VycyB3ZXJlIHRha2VuIGZyb20gdGhlIHBva2Vtb24gY29sb3VyIHBpY2tlciBvZiBwaWthY2h1OiA8aHR0cHM6Ly9wb2tlcGFsZXR0ZXMuY29tLyNwaWthY2h1Pi4KCmBgYHtyfQpnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjYzUyMDE4IiwgIiM0MTQxNGEiKSkKYGBgCgpOb3cgd2UgY2FuIGFkZCBhIHRpdGxlIGFuZCBzYXZlIHRoZSBwbG90ISBXaGVuIHNhdmluZyB0aGUgcGxvdCB3ZSBoYXZlIG1hbnVhbGx5IGFkanVzdGVkIHRoZSB3aWR0aCBvZiB0aGUgcGxvdC4gWW91IGNhbiBhbHNvIGNoYW5nZSB0aGUgaGVpZ2h0LiAKCmBgYHtyfQplbGVjdHJpY19wb2tlbW9uX2JveCA8LSBnZ3Bsb3QoZWxlY3RyaWNfcG9rZW1vbiwgYWVzKHggPSBnZW5lcmF0aW9uLCB5ID0gdG90YWwpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI2Y2ZTY1MiIsIG91dGxpZXIuc2hhcGUgPSBOQSkgKwogIGdlb21faml0dGVyKGFlcyhjb2xvdXIgPSBsZWdlbmRhcnkpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCIjYzUyMDE4IiwgIiM0MTQxNGEiKSkgKwogIGxhYnModGl0bGUgPSAiU3VtbWFyeSBvZiBlbGVjdHJpYyBwb2tlbW9uIGZvciBlYWNoIGdlbmVyYXRpb24iKSArCiAgdGhlbWVfYncoKQoKZWxlY3RyaWNfcG9rZW1vbl9ib3gKCmdnc2F2ZSgiZWxlY3RyaWNfcG9rZW1vbl9ib3gucG5nIiwgZWxlY3RyaWNfcG9rZW1vbl9ib3gsCiAgICAgICB3aWR0aCA9IDUuNSkKYGBgCgoKIyMgSW1wcm92aW5nIHlvdXIgYm94IHBsb3RzIGV4ZXJjaXNlCgpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIGxvb2sgYXQgdmFjY2luZXMhIFdlIHdpbGwgbG9vayBhdCAxMCBjb3VudHJpZXMgdG8gc2VlIHRoZSBkaWZmZXJlbmNlIGluIHZhY2NpbmUgZGlzdHJpYnV0aW9uOyA1IGhhdmUgbG93IGdkcCBhbmQgNSBoYXZlIGhpZ2ggZ2RwLiBUaGUgZGF0YSB3aWxsIGJlIHByZS1wcmVwYXJlZCBmb3IgeW91LiBXZSBoYXZlIG1hZGUgYSB2ZWN0b3Igd2l0aCB0aGUgY291bnRpZXMgdGhhdCBoYXZlIGhpZ2ggYW5kIGxvdyBnZHAuIFRoZW4gd2UgaGF2ZSBmaWx0ZXJlZCBvdXIgY292aWQgZGF0YSBieSB0aGlzIHZlY3RvciwgYW5kIG1hZGUgdGhlIGxvY2F0aW9uIGEgb3JkZXJlZCBmYWN0b3IuCgoxKSBNYWtlIGEgYm94IHBsb3QgdXNpbmcgdGhlICpjb3ZpZF9zZWxlY3RfY291bnRyaWVzKiBkYXRhLCB3aXRoIHggPSBsb2NhdGlvbiBhbmQgeSA9IHRvdGFsX3ZhY2NpbmF0aW9uc19wZXJfaHVuZHJlZC4gQmUgc3VyZSB0byBpbmNsdWRlIGBnZW9tX2ppdHRlcigpYC4KMikgTm93IGltcHJvdmUgdGhlIGxvb2sgb2YgeW91ciBib3ggcGxvdCEgQ2hhbmdlIHRoZSBjb2xvdXIgb2YgdGhlIGJveGVzIGFuZCB0aGUgcG9pbnRzLCBtYWtlIHRoZSBwb2ludHMgbW9yZSB0cmFuc3BhcmVudCwgcmVtb3ZlIHRoZSBvdXRsaWVycywgY2hhbmdlIHRoZSB0aGVtZSwgYW5kIGZsaXAgdGhlIGNvLW9yZGluYXRlcy4gCjMpIE1ha2UgYW5vdGhlciBib3ggcGxvdCB0aGUgc2FtZSB3YXkgYnV0IHVzZSB0aGUgcGVvcGxlX2Z1bGx5X3ZhY2NpbmF0ZWQgdmFyaWFibGUgYXMgeW91ciB5IGF4aXMuIAo0KSBHaXZlIGJvdGggeW91ciBib3ggcGxvdHMgYSB0aXRsZSBhbmQgY2hhbmdlIHRoZSBheGlzIGxhYmVscyAoaWYgeW91IHdhbnQpLgo1KSBTYXZlIHlvdXIgcGxvdHMgdXNpbmcgYGdnc2F2ZSgpYC4gWW91IHdpbGwgbmVlZCB0byBhc3NpZ24gdGhlIHBsb3RzIHRvIGEgdmFyaWFibGUgZmlyc3QuIAoKYGBge3J9CiMgTWFrZSB2ZWN0b3Igd2l0aCBsb3cgYW5kIGhpZ2ggZ2RwIGNvdW50cmllcwpoaWdoX2xvd19nZHAgPC0gYygiU2llcnJhIExlb25lIiwgIkV0aGlvcGlhIiwiWWVtZW4iLCAKICAgICAgICAgICAgICAgICAgIlphbWJpYSIsICJOZXBhbCIsICJTd2VkZW4iLCAiQXVzdHJhbGlhIiwKICAgICAgICAgICAgICAgICAgIlNhdWRpIEFyYWJpYSIsICJHZXJtYW55IiwgIlVuaXRlZCBLaW5nZG9tIikKCiMgT25seSBpbmNsdWRlIGxvY2F0aW9ucyBpbiBoaWdoX2xvd19nZHAKIyBNYWtlIGxvY2F0aW9uIGEgZmFjdG9yLCBvcmRlcmVkIGJ5IGhpZ2hfbG93X2dkcApjb3ZpZF9zZWxlY3RfY291bnRyaWVzIDwtIGNvdmlkICU+JQogIGZpbHRlcihsb2NhdGlvbiAlaW4lIGhpZ2hfbG93X2dkcCkgJT4lCiAgbXV0YXRlKGxvY2F0aW9uID0gZmFjdG9yKGxvY2F0aW9uLCBsZXZlbHMgPSBoaWdoX2xvd19nZHApKQoKIyB5b3VyIGNvZGUgaGVyZQoKCmBgYAoKIyBEaXNwbGF5aW5nIGRpc3RyaWJ1dGlvbnMgd2l0aCBoaXN0b2dyYW1zCgpIaXN0b2dyYW1zIGFyZSBncmVhdCBmb3IgdmlzdWFsaXNpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBudW1lcmljIGRhdGEuIEhpc3RvZ3JhbXMgaGF2ZSBvbmUgbnVtZXJpY2FsIHZhcmlhYmxlIGFzIHRoZWlyIGlucHV0LiAKClRvIG1ha2UgYSBoaXN0b2dyYW0gd2l0aCBnZ3Bsb3Qgd2UgcHJvdmlkZSBhIG51bWVyaWNhbCB2YWx1ZSB0byBvdXIgeCBheGlzLCBhbmQgdXNlIHRoZSBgZ2VvbV9oaXN0b2dyYW0oKWAgZ2VvbS4gSW4gdGhlIGV4YW1wbGUgd2UgYXJlIHVzaW5nIGFsbCB0aGUgcG9rZW1vbiBkYXRhIGFuZCBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHRvdGFsIGNvbHVtbi4gCmBgYHtyfQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCkpICsKICBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKV2UgY2FuIGFkanVzdCB0aGUgc2l6ZSBvZiB0aGUgKmJpbnMqIG9mIG91ciBwbG90IHdpdGggdHdvIG1ldGhvZHMsIGNoYW5naW5nIHRoZSBiaW53aWR0aCBvciBzZWxlY3RpbmcgdGhlIGFtb3VudCBvZiBiaW5zLiBXaGVuIHdlIHRhbGsgYWJvdXQgYmlucyB3aXRoIGhpc3RvZ3JhbXMgaXQgcmVmZXJzIHRvIHRoZSBzaXplIG9mIGVhY2ggYmFyOyB0aGUgbGFyZ2VyIHRoZSBiYXIgdGhlIG1vcmUgZGF0YSBvbiB0aGUgeCBheGlzIGlzIGluY2x1ZGVkLiAKClRoZSBmaXJzdCBleGFtcGxlIHVzZXMgYGJpbndpZHRoYC4gVGhlIG51bWJlciB5b3UgcHJvdmlkZSBpcyBkaXJlY3RseSByZWxhdGVkIHRvIHlvdXIgeCBheGlzLiBJbiBvdXIgZXhhbXBsZSB3ZSBhcmUgdXNpbmcgdGhlIHRvdGFsIGNvbHVtbiB3aGljaCBnb2VzIHVwIHRvIDc1NC4gSWYgd2UgaGF2ZSBgYmlud2lkdGggPSA4YCwgdGhlbiA4IGRhdGEgcG9pbnRzIHdpbGwgYmUgaW5jbHVkZWQgaW4gZWFjaCBiaW4uIFJ1biB0aGUgdHdvIGV4YW1wbGVzIGJlbG93IHdpdGggYSBzbWFsbGVyIGFuZCBsYXJnZXIgYmlud2lkdGggdG8gc2VlIHRoZSByZXN1bHRzLiAKYGBge3J9CiMgc3VtbWFyeSBzdGF0cyBmb3IgdG90YWwgY29sdW1uCnN1bW1hcnkocG9rZW1vbiR0b3RhbCkKCiMgYmlud2lkdGggb2YgOApnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDgpICsKICBsYWJzKHRpdGxlID0gIlNtYWxsIGJpbndpZHRoICg4KSIpCgojIGJpbndpZHRoIG9mIDUwCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTApICsKICBsYWJzKHRpdGxlID0gIkxhcmdlciBiaW53aWR0aCAoNTApIikKYGBgCgpUaGUgb3RoZXIgbWV0aG9kIGlzIHRvIHNlbGVjdCB0aGUgbnVtYmVyIG9mIGJpbnMgdG8gdXNlLCB1c2luZyB0aGUgYGJpbnNgIGFyZ3VtZW50LiBUaGUgbW9yZSBiaW5zIHdlIHVzZSwgdGhlIGxlc3MgZGF0YSB3aWxsIGJlIGNvbnRhaW5lZCBpbiBlYWNoIGJpbi4gSW4gdGhlIGV4YW1wbGUgYmVsb3cgd2UgaGF2ZSBiaW5zIHdpdGggbG90cyBvZiBkYXRhIGBiaW5zID0gMTBgIGFuZCBiaW5zIHdpdGggbGVzcyBkYXRhIGBiaW5zID0gNTBgLiBXaGljaCBkbyB5b3UgdGhpbmsgaXMgYmVzdD8KCmBgYHtyfQojIHVzaW5nIDEwIGJpbnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdG90YWwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwKSArCiAgbGFicyh0aXRsZSA9ICJMZXNzIGJpbnMgPSBtb3JlIGRhdGEgaW4gZWFjaCBiaW4iKQoKIyB1c2luZyA1MCBiaW5zCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA1MCkgKwogIGxhYnModGl0bGUgPSAiTW9yZSBiaW5zID0gbGVzcyBkYXRhIGluIGVhY2ggYmluIikKYGBgCgpJdCBjYW4gYmUgaGVscGZ1bCB0byBjb2xvdXIgeW91ciBoaXN0b2dyYW0gYnkgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhpcyB3b3JrcyB0aGUgc2FtZSBhcyBhIGJveCBwbG90LCB1c2luZyB0aGUgYGZpbGxgIGFyZ3VtZW50LiBJbiB0aGUgZXhhbXBsZSB3ZSBoYXZlIGZpbGxlZCBvdXIgaGlzdG9ncmFtIGJ5IHRoZSBsZWdlbmRhcnkgY2F0ZWdvcnkuIAoKYGBge3J9CmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjApCmBgYAoKQW5vdGhlciB1c2VmdWwgbWV0aG9kIGlzIHRvIHVzZSAqZmFjZXRzKiwgd2hpY2ggc3BsaXQgdXAgeW91ciBkYXRhIGJ5IGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgYW5kIHByZXNlbnRzIHRoZW0gaW4gYSBncmlkIGxpa2UgZm9ybWF0aW9uLiAKClRoZXJlIGFyZSB0d28gdGVjaG5pcXVlcyBpbiBnZ3Bsb3QgdG8gbWFrZSBmYWNldHMsIHVzaW5nIGBmYWNldF9ncmlkKClgIG9yIGBmYWNldF93cmFwKClgLiBUbyB1c2UgYGZhY2V0X2dyaWQoKWAgd2UgZGVmaW5lIGlmIHdlIHdhbnQgdG8gZGlzcGxheSBvdXIgZGF0YSByb3ctd2lzZSAoYHJvd3MgPSBgKSBvciBjb2x1bW4td2lzZSAoYGNvbHMgPSBgKS4gV2hlbiBkZWZpbmluZyB3aGljaCBjb2x1bW4gdG8gc3BsaXQgb3VyIGRhdGEgYnkgd2UgbmVlZCB0byB1c2UgdGhlIGB2YXJzKClgIGZ1bmN0aW9uLiBTZWUgdGhlIHR3byBleGFtcGxlcyBiZWxvdyBvbiBob3cgdG8gZG8gYSByb3cgb3IgY29sdW1uIGZhY2V0IGdyaWQuIAoKYGBge3J9CiMgcm93LXdpc2UgZGlzcGxheQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhsZWdlbmRhcnkpKSArCiAgbGFicyh0aXRsZSA9ICJSb3ctd2lzZSBmYWNldCBncmlkIikKCiMgY29sdW1uLXdpc2UgZGlzcGxheQpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhsZWdlbmRhcnkpKSArCiAgbGFicyh0aXRsZSA9ICJjb2x1bW4td2lzZSBmYWNldCBncmlkIikKYGBgCgpUaGUgb3RoZXIgb3B0aW9uIGlzIGBmYWNldF93cmFwKClgLCB3aGljaCBieSBkZWZhdWx0IG9ubHkgbmVlZHMgdGhlIGNvbHVtbiB5b3Ugd2FudCB0byBzcGxpdCB5b3VyIGRhdGEgYnkuIEl0IGRvZXMgYWxsb3cgZXh0cmEgc3BlY2lmaWNhdGlvbiB3aXRoIHRoZSBgbnJvd2AgYW5kIGBuY29sYCBmdW5jdGlvbnMsIGFsbG93aW5nIHlvdSB0byBkZWZpbmUgaG93IG1hbnkgcm93cyBhbmQgY29sdW1ucyB0byBkaXNwbGF5LiAKCkluIHRoZSBleGFtcGxlcyBiZWxvdyB3ZSBzaG93IHRoZSBkZWZhdWx0IGBmYWNldF93cmFwYCwgYW5kIGhvdyB0byBhZGp1c3QgdGhlIGNvbHVtbiBvciByb3cgc3BlY2lmaWNhdGlvbi4gV2UgaGF2ZSB1c2VkIHRoZSBnZW5lcmF0aW9uIGNvbHVtbiBhcyBpdCBoYXMgbW9yZSBncm91cHMuICAKYGBge3J9CiMgZGVmYXVsdCBmYWNldF93cmFwCmdncGxvdChwb2tlbW9uLCBhZXMoeCA9IHRvdGFsLCBmaWxsID0gbGVnZW5kYXJ5KSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjApICsKICBmYWNldF93cmFwKHZhcnMoZ2VuZXJhdGlvbikpICsKICBsYWJzKHRpdGxlID0gIkRlZmF1bHQgZmFjZXQgd3JhcCIpCgojIDQgcm93cwpnZ3Bsb3QocG9rZW1vbiwgYWVzKHggPSB0b3RhbCwgZmlsbCA9IGxlZ2VuZGFyeSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwKSArCiAgZmFjZXRfd3JhcCh2YXJzKGdlbmVyYXRpb24pLAogICAgICAgICAgICAgbnJvdyA9IDQpICsKICBsYWJzKHRpdGxlID0gIkZhY2V0IHdyYXAgd2l0aCA0IHJvd3MiKQoKIyA0IGNvbHVtbnMKZ2dwbG90KHBva2Vtb24sIGFlcyh4ID0gdG90YWwsIGZpbGwgPSBsZWdlbmRhcnkpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyMCkgKwogIGZhY2V0X3dyYXAodmFycyhnZW5lcmF0aW9uKSwKICAgICAgICAgICAgIG5jb2wgPSA0KSArCiAgbGFicyh0aXRsZSA9ICJGYWNldCB3cmFwIHdpdGggNCBjb2x1bW5zIikKYGBgCgoKIyMgRGlzcGxheWluZyBkaXN0cmlidXRpb25zIGV4ZXJjaXNlCgpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIGJlIG1ha2luZyBhIGhpc3RvZ3JhbSBvZiB1c2luZyB0aGUgcGVvcGxlX2Z1bGx5X3ZhY2NpbmF0ZWRfcGVyX2h1bmRyZWQgY29sdW1uIGZvciBlYWNoIGNvbnRpbmVudCAKCi0gTWFrZSBhIGhpc3RvZ3JhbSB3aXRoIHBlb3BsZV9mdWxseV92YWNjaW5hdGVkX3Blcl9odW5kcmVkIGFzIHlvdXIgeCBheGlzCi0gQWRkIGEgZmlsbCBhcmdtZW50IHdpdGggY29udGluZW50Ci0gQWRqdXN0IHRoZSBgYmlud2lkdGhgIG9yIGBiaW5zYCAoZS5nLiBgYmlud2lkdGggPSA1YCBsb29rcyBnb29kKQotIFVzaW5nIFJDb2xvdXJCcmV3ZXIsIGFkanVzdCB0aGUgY29sb3VycyB1c2VkIGluIGZpbGwKCkhpbnQ6IHlvdSB3aWxsIGhhdmUgdG8gcmVtb3ZlIHRoZSBuYSB2YWx1ZXMgZnJvbSBjb250aW5lbnQgYmVmb3JlIHBsb3R0aW5nLCBlLmcuIGBjb3ZpZCAlPiUgZmlsdGVyKCFpcy5uYShjb250aW5lbnQpKWAKCkhpbnQ6IFlvdSBjYW4gcGlwZSBmcm9tIHlvdXIgZmlsdGVyIGZ1bmN0aW9uIHN0cmFpZ2h0IGludG8gZ2dwbG90MiEKCkhpbnQ6IFRvIGNoYW5nZSB0aGUgZmlsbCBjb2xvdXJzIHlvdSBjYW4gdXNlIGBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gImEgcGFsZXR0ZSIpYAoKSGludDogVXNlIGBicmV3ZXIucGFsLmluZm9gIHRvIGZpbmQgUkNvbG9yQnJld2VyIHBhbGV0dGVzCgpgYGB7cn0KIyB5b3VyIGNvZGUgaGVyZQoKYGBgCgoKIyBXb3JraW5nIHdpdGggdGhlIGRhdGUgZGF0YSB0eXBlIHdpdGggbHVicmlkYXRlCgpXb3JraW5nIHdpdGggdGhlIGRhdGUgZGF0YSB0eXBlIHdoZW4gcHJvZ3JhbW1pbmcgY2FuIGJlIGEgYml0IHRyaWNreSBmb3IgbWFueSByZWFzb25zLiBUaGVyZSBhcmUgZGlmZmVyZW50IGZvcm1hdHMsIHRpbWUgem9uZXMsIGFuZCB0aGUgY2hhbGxlbmdlIGV4dHJhY3RpbmcgaW5mb3JtYXRpb24gZnJvbSB0aGUgZGF0ZS4gRm9ydHVuYXRlbHksIHRoZSBgbHVicmlkYXRlYCBwYWNrYWdlIGNvbWVzIHRvIHRoZSByZXNjdWUhIAoKVGhlcmUgYXJlIHRocmVlIHR5cGVzIG9mIGRhdGUgZGF0YSB0eXBlOiBkYXRlICgyMDEwLTA5LTAxKSwgdGltZSAoMTU6MDg6NTIgQlNUKSwgZGF0ZS10aW1lICgyMDEwLTA5LTAxIDE1OjA4OjUyIEJTVCkuIEZvciB0aGlzIHdvcmtzaG9wIHdlIHdpbGwgYmUgZm9jdXNpbmcgb24gdGhlIGRhdGUgdHlwZSBhcyBpdCBpcyB0aGUgbW9zdCBjb21tb24uIAoKWW91IGNhbiBmaW5kIG91dCB0b2RheSdzIGRhdGUgKG1vcmUgdXNlZnVsIHRoYW4gaXQgc291bmRzKSBvciB0aGUgZGF0ZSBhbmQgdGltZSB1c2luZyB0aGUgYHRvZGF5KClgIG9yIGBub3coKWAgZnVuY3Rpb25zLiAKYGBge3J9CiMgbWFrZSBzdXJlIGRwbHlyIGFuZCBsdWJyaWRhdGUgYXJlIGxvYWRlZApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCiMgZ2V0IHRvZGF5J3MgZGF0ZQp0b2RheSgpCiMgdG9kYXkncyBkYXRlIGFuZCB0aW1lCm5vdygpCgojIG1ha2UgdG9kYXkncyBkYXRlIGEgdmFyaWFibGUKdG9kYXlfZGF0ZSA8LSB0b2RheSgpCmBgYAoKQSBncmVhdCBmZWF0dXJlIG9mIGx1YnJpZGF0ZSBpcyBleHRyYWN0aW5nIHRoZSB5ZWFyLCBtb250aCwgZGF5LCBvciB3ZWVrIGRheSBpbmZvcm1hdGlvbiBmcm9tIHlvdXIgZGF0ZS4gV2UgY2FuIHRlc3QgaXQgb3V0IG9uIHRvZGF5J3MgZGF0ZS4gUnVuIHRoZSBjb2RlIHRvIHNlZSBob3cgdGhlIG91dHB1dC4gCgpgYGB7cn0KIyB5ZWFyCnllYXIodG9kYXlfZGF0ZSkKIyBtb250aAptb250aCh0b2RheV9kYXRlKQptb250aCh0b2RheV9kYXRlLCBsYWJlbCA9IFRSVUUpCiMgd2Vlawp3ZWVrKHRvZGF5X2RhdGUpCiMgZGF5CmRheSh0b2RheV9kYXRlKQojIHdlZWtkYXkKd2RheSh0b2RheV9kYXRlKQp3ZGF5KHRvZGF5X2RhdGUsIGxhYmVsID0gVFJVRSkKYGBgCgpOb3RpY2UgdGhhdCBmb3IgdGhlIGBtb250aGAgYW5kIGB3ZGF5YCBmdW5jdGlvbnMgd2UgaGF2ZSB0aGUgb3B0aW9uIHRvIGFkZCBsYWJlbHMuIFRoaXMgY2FuIGJlIHZlcnkgdXNlZnVsLCBtYWtpbmcgeW91ciBtb250aCBvciB3ZWVrIGRheSBvdXRwdXRzIG1vcmUgcmVhZGFibGUuIAoKRm9yIHRoZSByZXN0IG9mIHRoZSBleGFtcGxlcyB3ZSB3aWxsIHVzZSBzb21lIHJhbmRvbWlzZWQgbWFkZSB1cCBkYXRhIGNvbnRhaW5pbmcgZGFpbHkgc2xlZXAsIGFuZCBzdGVwIGluZm9ybWF0aW9uLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gc2VlIHRoZSBkYXRhLiAKCipub3RlOiB0byBtYWtlIHRoaXMgZGF0YSB3ZSBoYXZlIHVzZWQgcmFuZG9taXNhdGlvbiBmdW5jdGlvbnM6IGBzYW1wbGVgLCBgcnVuaWZgIGFuZCBgcm5vcm1gLCBpZiB5b3UgYXJlIGludGVyZXN0ZWQgbG9vayB0aGVtIHVwIHRvIHNlZSBob3cgdGhleSB3b3JrKgpgYGB7cn0KIyBtYWtlIHNvbWUgcmFuZG9tIGRhdGEKZGYgPC0gZGF0YS5mcmFtZSgKICBkYXRlID0gc2VxKGFzLkRhdGUoIjIwMTktMDEtMDEiKSwgYXMuRGF0ZSgiMjAyMS0xMi0wMSIpLCBieSA9ICJkYXlzIiksCiAgaG91cnNfc2xlZXAgPSByb3VuZChybm9ybSgxMDY2LCBtZWFuID0gOSwgc2QgPSAxLjUpKSwKICBzdGVwcyA9IHJvdW5kKHJub3JtKDEwNjYsIG1lYW4gPSA4MDAwLCBzZCA9IDIwMDApKQopCgpoZWFkKGRmKQpgYGAKCldlIGNhbiBub3cgdXNlIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiB0byBtYWtlIGEgeWVhciwgbW9udGgsIHdlZWssIGRheSwgYW5kIHdlZWsgZGF5IGNvbHVtbi4gCgpgYGB7cn0KZGYgPC0gZGYgJT4lCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGRhdGUpLAogICAgICAgICBtb250aCA9IG1vbnRoKGRhdGUsIGxhYmVsID0gVFJVRSksCiAgICAgICAgIHdlZWsgPSB3ZWVrKGRhdGUpLAogICAgICAgICBkYXkgPSBkYXkoZGF0ZSksCiAgICAgICAgIHdlZWtfZGF5ID0gd2RheShkYXRlLCBsYWJlbCA9IFRSVUUpKQoKaGVhZChkZikKCiMgc2VlIHRoZSBicmVha2Rvd24gb2YgdGhlIGRhdGUKZGZbMToyLCBjKCJkYXRlIiwgInllYXIiLCAibW9udGgiLCAid2VlayIsICJkYXkiLCAid2Vla19kYXkiKV0KYGBgCgpCcmVha2luZyB0aGUgZGF0ZSBkb3duIGluIHRoaXMgd2F5IGFsbG93cyB1cyB0byBkbyBzb21lIGFnZ3JlZ2F0aW9uIG9mIG91ciBkYXRhIGJ5IHRoZSB5ZWFyLCBtb250aCwgd2VlaywgZGF5LCBvciB3ZWVrZGF5ISBJbiB0aGUgZXhhbXBsZXMgYmVsb3cgd2UgaGF2ZSBzaG93biB5ZWFyIGFuZCB3ZWVrZGF5LgoKYGBge3J9CiMgYWdncmVnYXRlIGJ5IHllYXIKZGYgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXNlKGF2Z19zbGVlcCA9IG1lYW4oaG91cnNfc2xlZXApLAogICAgICAgICAgICBhdmdfc3RlcHMgPSBtZWFuKHN0ZXBzKSwKICAgICAgICAgICAgdG90YWxfc3RlcHMgPSBzdW0oc3RlcHMpKQoKIyBhZ2dyZWdhdGUgYnkgd2VlayBkYXkKZGYgJT4lCiAgZ3JvdXBfYnkod2Vla19kYXkpICU+JQogIHN1bW1hcmlzZShhdmdfc2xlZXAgPSBtZWFuKGhvdXJzX3NsZWVwKSwKICAgICAgICAgICAgYXZnX3N0ZXBzID0gbWVhbihzdGVwcyksCiAgICAgICAgICAgIHRvdGFsX3N0ZXBzID0gc3VtKHN0ZXBzKSkKYGBgIAoKVGhlcmUgYXJlIG1vcmUgZnVuY3Rpb25zIGZyb20gdGhlIGx1YnJpZGF0ZSBwYWNrYWdlIHRoYXQgd2Ugd29uJ3QgYmUgYWJsZSB0byBjb3ZlciBpbiB0aGlzIHNlc3Npb24sIHNvIGRvIGhhdmUgYSBsb29rIGF0IHRoZSBwYWNrYWdlIHdlYnNpdGUgZm9yIG1vcmUgaW5mb3JtYXRpb24gLSA8aHR0cHM6Ly9sdWJyaWRhdGUudGlkeXZlcnNlLm9yZy9pbmRleC5odG1sPiAtIGFuZCBjaGVja291dCB0aGUgUiBmb3IgRGF0YSBTY2llbmNlIGNoYXB0ZXIgb24gZGF0ZXMgLSA8aHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRlcy1hbmQtdGltZXMuaHRtbD4uIAoKIyMgbHVicmlkYXRlIGV4ZXJjaXNlCgpVc2luZyB0aGUgZXhhbXBsZXMgYWJvdmUsIGV4dHJhY3QgeWVhciwgbW9udGgsIGRheSwgZGF5IG9mIHdlZWsgZnJvbSBjb3ZpZCBkYXRhLCBhbmQgZG8gYW4gYWdncmVnYXRpb24hIAoKMSkgQWRkIG5ldyBjb2x1bW5zIHRvIHlvdXIgY292aWQgZGF0YSBmb3IgeWVhciwgbW9udGgsIHdlZWssIGRheSBhbmQgd2Vla19kYXkuIFRyeSB0byBhZGQgbGFiZWxzIHRvIG1vbnRoIGFuZCB3ZWVrX2RheS4gCjIpIEFnZ3JlZ2F0ZSB5b3VyIGNvdmlkIGRhdGEgYnkgeWVhciBhbmQgbW9udGggdG8gZmluZCB0aGUgbWVhbiB0b3RhbCBjYXNlcyBwZXIgbWlsbGlvbiBhbmQgbWVhbiB0b3RhbCBkZWF0aHMgcGVyIG1pbGxpb24uIAozKSBQcmludCBvdXQgdGhlIHJlc3VsdC4gCmBgYHtyfQojIHlvdXIgY29kZSBoZXJlCgojIHNlcGFyYXRlIGRhdGUgY29sdW1uCgoKIyBtYWtlIHllYXIgYW5kIG1vbnRoIGFnZ3JlZ2F0ZQoKYGBgCgoKIyBUaW1lIHNlcmllcyBwbG90cwoKVGltZSBzZXJpZXMgcGxvdHMgdmlzdWFsaXNlIGRhdGEgb3ZlciBhIHBlcmlvZCBvZiB0aW1lLCB3aGljaCBjYW4gYmUgaG91cmx5LCBkYWlseSwgd2Vla2x5LCBtb250aGx5LCBvciB5ZWFybHkuIEl0IGlzIGEgZ3JlYXQgd2F5IHRvIHZpZXcgdHJlbmRzIG92ZXIgdGltZS4gV2hlbiBwbG90dGluZyBhIHRpbWUgc2VyaWVzLCB0aGUgeCBheGlzIGlzIHRoZSBkYXRlIGFuZCB0aGUgeSBheGlzIGlzIHlvdXIgbWVhc3VyZS4gCgpUaGUgbW9zdCBzaW1wbGUgZm9ybSBvZiBhIHRpbWUgc2VyaWVzIHZpc3VhbGlzYXRpb24gaW4gUiBpcyB0byB1c2UgYW4gdW5lZGl0ZWQgZGF0ZSB2YXJpYWJsZS4gVXNpbmcgb3VyIGV4YW1wbGUgZGF0YSAoYGRmYCkgd2Ugd2lsbCB2aXN1YWxpc2UgaG93IHN0ZXBzIGhhdmUgY2hhbmdlZCBlYWNoIGRheS4gCmBgYHtyfQojIGRhaWx5IHRpbWUgc2VyaWVzCmRmICU+JQogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBzdGVwcykpICsKICBnZW9tX2xpbmUoKQpgYGAKCkFzIHdlIGNhbiBzZWUgaXQgaXMgcHJldHR5IHZhcmlhYmxlIGhvdyBtYW55IHN0ZXBzIGFyZSB0YWtlbiBlYWNoIGRheSwgYXMgeW91IG1pZ2h0IGV4cGVjdC4gVGhlcmUgaXMgYSBsb3Qgb2YgZGF0YSBoZXJlIHNvIGl0IGlzIGhhcmQgdG8gc2VlIGFueSByZWFsIHBhdHRlcm5zLCBpdCBqdXN0IGxvb2tzIGxpa2Ugbm9pc2UhIFRvIHNvbHZlIHRoaXMgd2UgY2FuIGFnZ3JlZ2F0ZSBvdXIgZGF0YSBieSB0aGUgeWVhciwgdGhlIG1vbnRoIG9yIHRoZSB3ZWVrIHRvIHNlZSBpZiB3ZSBjYW4gZ2V0IGFueSBtb3JlIGluc2lnaHRzLiAKCkZvciB0aGUgZXhhbXBsZSBkYXRhIHdlIGhhdmUgaXQgbWlnaHQgYmUgaW50ZXJlc3RpbmcgdG8gc2VlIHRoZSBhdmVyYWdlIG9mIGhvdyBtYW55IHN0ZXBzIGFyZSB0YWtlbiBvbiBhdmVyYWdlIGVhY2ggbW9udGgsIGFuZCB0byBhbHNvIGNvbXBhcmUgdGhpcyB5ZWFyIG9uIHllYXIuCgpXZSBmaXJzdCBhZ2dyZWdhdGUgb3VyIGRhdGEsIGdyb3VwaW5nIGJ5IHRoZSBtb250aCBhbmQgeWVhciBjb2x1bW5zIHdlIG1hZGUgd2l0aCB0aGUgbHVicmlkYXRlIHBhY2thZ2UsIGZpbmQgdGhlIGF2ZXJhZ2Ugc3RlcHMsIGFuZCBjb252ZXJ0IHRoZSB5ZWFyIGNvbHVtbiBpbnRvIGEgZmFjdG9yIHRvIG1ha2UgcGxvdHRpbmcgZWFzaWVyOyBtb250aCBpcyBhbHJlYWR5IGEgZmFjdG9yLiAKYGBge3J9CiMgYWdncmVnYXRlZCB0aW1lIHNlcmllcyBieSBtb250aAptb250aGx5X3N0ZXBzIDwtIGRmICU+JQogIGdyb3VwX2J5KG1vbnRoLCB5ZWFyKSAlPiUKICBzdW1tYXJpc2UoYXZnX3N0ZXBzID0gbWVhbihzdGVwcykpICU+JQogIG11dGF0ZSh5ZWFyID0gZmFjdG9yKHllYXIpKQoKbW9udGhseV9zdGVwcwpgYGAKCk5vdyB3ZSBjYW4gbWFrZSBhIHRpbWUgc2VyaWVzIGJ5IG1vbnRoISBJdCBpcyBvZnRlbiBoZWxwZnVsIHdoZW4gdXNpbmcgYGdlb21fbGluZSgpYCB0byBhbHNvIHBhaXIgaXQgd2l0aCBgZ2VvbV9wb2ludCgpYCBzbyB3ZSBjYW4gc2VlIGVhY2ggZGF0YSBwb2ludCBjbGVhcmx5IGFzIHdlbGwgYXMgc2VlaW5nIHRoZSB0cmVuZHMgd2l0aCBzaG93biBieSB0aGUgbGluZXMuIAoKYGBge3J9CmdncGxvdChtb250aGx5X3N0ZXBzLAogICAgICAgYWVzKHggPSBtb250aCwgeSA9IGF2Z19zdGVwcykpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKVGhhdCBkaWRuJ3Qgd29yayBhcyBleHBlY3RlZCEgQXMgb3VyIGRhdGEgaXMgZ3JvdXBlZCBieSB5ZWFyIGFuZCBtb250aCB3ZSBuZWVkIHRvIHVzZSB0aGUgYGdyb3VwID0gYCBhcmd1bWVudCB0byB0ZWxsIGdncGxvdCB3ZSB3YW50IHRvIGNvbm5lY3QgdGhlIG1vbnRocyB1cC4gCgpCeSBhZGRpbmcgYGdyb3VwID0geWVhcmAgb3VyIHBsb3Qgd2lsbCBub3cgbG9vayBsaWtlIGEgdGltZSBzZXJpZXMsIHJ1biB0aGUgY29kZSB0byBjaGVjayBpdCBvdXQuIAoKYGBge3J9CmdncGxvdChtb250aGx5X3N0ZXBzLAogICAgICAgYWVzKHggPSBtb250aCwgeSA9IGF2Z19zdGVwcywKICAgICAgICAgICBncm91cCA9IHllYXIpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKQpgYGAKCkl0IHdvdWxkIGFsc28gYmUgaGVscGZ1bCB0byBzZWUgd2hhdCB5ZWFyIGVhY2ggbGluZSByZXByZXNlbnRzLiBXZSBhZGQgdGhlIGBjb2xvdXIgPSB5ZWFyYCBhcmd1bWVudCBpbiBhcyB3ZWxsIHRvIHNob3cgdGhpcy4gCgpgYGB7cn0KZ2dwbG90KG1vbnRobHlfc3RlcHMsCiAgICAgICBhZXMoeCA9IG1vbnRoLCB5ID0gYXZnX3N0ZXBzLAogICAgICAgICAgIGdyb3VwID0geWVhciwgY29sb3VyID0geWVhcikpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKT3VyIHBsb3QgaXMgc3RpbGwgbG9va2luZyBhIGxpdHRsZSBidXN5IHNvIHdlIGNhbiB1c2UgZmFjZXRzIHRvIHNwbGl0IG91ciBkYXRhIGJ5IHllYXIuIFdlJ3ZlIHVzZWQgYGZhY2V0X3dyYXBgIGhlcmUgd2l0aCAzIHJvd3MuIAoKYGBge3J9CmdncGxvdChtb250aGx5X3N0ZXBzLAogICAgICAgYWVzKHggPSBtb250aCwgeSA9IGF2Z19zdGVwcywKICAgICAgICAgICBncm91cCA9IHllYXIsIGNvbG91ciA9IHllYXIpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCh2YXJzKHllYXIpLCBucm93ID0gMykKYGBgCgpGaW5hbGx5LCB3ZSBjYW4gbWFrZSBhIGZldyBmaW5hbCBhZGp1c3RtZW50cyBhbmQgd2UgaGF2ZSBhIG5pY2UgdmlzdWFsaXNhdGlvbiB0aGF0IHNob3dzIGF2ZXJhZ2Ugc3RlcCBjb3VudCBwZXIgbW9udGggZm9yIHRoZSB5ZWFyIDIwMTkgdG8gMjAyMS4gQmVsb3cgaXMgYSBsaXN0IG9mIGFsbCB0aGUgYWRkaXRpb25zIG1ha2UgdG8gY2hhbmdlIHRoZSBsb29rIG9mIHRoZSBwbG90OgoKLSBDaGFuZ2VkIHRoZSBzaXplIG9mIHRoZSBsaW5lcyBhbmQgdGhlIHBvaW50cyB3aXRoIHRoZSBgc2l6ZSA9IGAgYXJndW1lbnQKLSBBZGRlZCBhIHRpdGxlIGFuZCBjaGFuZ2VkIHRoZSBheGlzIG5hbWVzCi0gQWRkZWQgYSBjb2xvdXIgc2NhbGUgZnJvbSB0aGUgUkNvbG9yQnJld2VyIHBhY2thZ2UKLSBDaGFuZ2VkIHRoZSB0aGVtZSB0byBkYXJrIGFuZCBjaGFuZ2VkIHRoZSBmb250IHRvIEF2ZW5pcgotIEFkanVzdGVkIHRoZSB5IGF4aXMgbGltaXRzCgpgYGB7cn0Kc3RlcF9jb3VudCA8LSBnZ3Bsb3QobW9udGhseV9zdGVwcywKICAgICAgIGFlcyh4ID0gbW9udGgsIHkgPSBhdmdfc3RlcHMsCiAgICAgICAgICAgZ3JvdXAgPSB5ZWFyLCBjb2xvdXIgPSB5ZWFyKSkgKwogIGdlb21fbGluZShzaXplID0gMi41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIGZhY2V0X3dyYXAodmFycyh5ZWFyKSwgbnJvdyA9IDMpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2Ugc3RlcCBjb3VudCBwZXIgbW9udGggZm9yIHRoZSB5ZWFyIDIwMTkgdG8gMjAyMSIsCiAgICAgICB4ID0gIk1vbnRoIiwgeSA9ICJBdmVyYWdlIHN0ZXBzIChtZWFuKSIsCiAgICAgICBjb2xvdXIgPSAiWWVhciIpICsKICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsKICB0aGVtZV9kYXJrKGJhc2VfZmFtaWx5ID0gIkF2ZW5pciIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyg3MDAwLCA5MDAwKSkgCgpzdGVwX2NvdW50CgpnZ3NhdmUoInN0ZXBfY291bnQucG5nIiwgc3RlcF9jb3VudCwgd2lkdGggPSA5KQpgYGAKCiMjIFRpbWUgc2VyaWVzIHBsb3RzIGV4ZXJjaXNlCgpGb3IgdGhpcyBleGVyY2lzZSB3ZSB3aWxsIGJlIGxvb2tpbmcgYXQgdGhlIHZhY2NpbmUgcm9sbCBvdXQgZm9yIFVuaXRlZCBLaW5nZG9tLCBJbmRpYSwgTmVwYWwsIElzcmFlbCwgR2VybWFueSwgYW5kIEF1c3RyYWxpYS4gRWFjaCBjb3VudHJ5IGhhcyBoYWQgc2xpZ2h0bHkgZGlmZmVyZW50IHJvbGwgb3V0cywgd2l0aCBJc3JhZWwgYmVpbmcgdGhlIGZhc3Rlc3QuIFdlIHdpbGwgYmUgbG9va2luZyBhdCB0aGUgd2VlayBieSB3ZWVrIHJvbGwgb3V0IGZvciAyMDIxLiAKCkRhdGEgcHJlcGFyYXRpb246IAoKMSkgTWFrZSBhIHZlY3RvciBjYWxsZWQgKnNlbF9jb3VudHJ5KiB0aGF0IGluY2x1ZGVzIFVuaXRlZCBLaW5nZG9tLCBJbmRpYSwgTmVwYWwsIElzcmFlbCwgR2VybWFueSwgYW5kIEF1c3RyYWxpYQoyKSBGaWx0ZXIgeW91ciBjb3ZpZCBkYXRhIHRvIGluY2x1ZGUgb25seSBsb2NhdGlvbnMgdGhhdCBhcmUgaW4geW91ciBzZWxfY291bnRyeSB2ZWN0b3IsIGFuZCBmaWx0ZXIgZm9yIHRoZSB5ZWFyIHRvIGJlIGVxdWFsIHRvIDIwMjEuIEFzc2lnbiB5b3VyIGZpbHRlcmVkIGRhdGEgdG8gYSB2YXJpYWJsZSBjYWxsZWQgKndlZWtseV92YXguKiAKMykgQWdncmVnYXRlIHlvdXIgKndlZWtseV92YXgqIGRhdGEgYnkgd2VlayBhbmQgbG9jYXRpb24gdG8gZmluZCB0aGUgbWVhbiBvZiB0aGUgYHBlb3BsZV92YWNjaW5hdGVkX3Blcl9odW5kcmVkYCBjb2x1bW4uIEFzc2lnbiB0aGUgcmVzdWx0IGJhY2sgdG8gKndlZWtseV92YXgqCjQpIE1ha2UgdGhlIHdlZWsgYW5kIGxvY2F0aW9uIGNvbHVtbnMgb2YgKndlZWtseV92YXgqIGZhY3RvcnMKIApQbG90dGluZzoKClVzaW5nIHlvdXIgKndlZWtseV92YXgqIGRhdGEgeW91IGhhdmUganVzdCBwcmVwYXJlZDoKCjEpIE1ha2UgYSB0aW1lIHNlcmllcyBwbG90IHdpdGggd2VlayBhcyB5b3VyIHggYXhpcyBhbmQgeW91ciBhZ2dyZWdhdGlvbiBvZiB0aGUgYHBlb3BsZV92YWNjaW5hdGVkX3Blcl9odW5kcmVkYCBjb2x1bW4gYXMgeW91ciB5IGF4aXMuIAoyKSBDb2xvdXIgYW5kIGdyb3VwIHlvdXIgZGF0YSBieSBsb2NhdGlvbi4gCjMpIE1ha2UgYW55IGFlc3RoZXRpYyBjaGFuZ2VzIHlvdSB0aGluayB3aWxsIG1ha2UgdGhlIHBsb3QgYmV0dGVyIGJhc2VkIG9uIHdoYXQgd2UgaGF2ZSBjb3ZlcmVkIHNvIGZhciwgc3VjaCBhcyBhZGRpbmcgdGl0bGVzLCBjaGFuZ2luZyBjb2xvdXJzLCBvciBhZGRpbmcgZmFjZXRzIChgZmFjZXRfZ3JpZCgpYCBvciBgZmFjZXRfd3JhcCgpYCkuCjQpIEFzc2lnbiB5b3VyIGZpbmFsIHBsb3QgdG8gYSB2YXJpYWJsZSBhbmQgc2F2ZSBpdCEgCgpIaW50OiBpZiB5b3VyIHggYXhpcyBpcyBsb29raW5nIHNxdWFzaGVkIG9yIGNyYW1wZWQsIHRyeSBhZGRpbmcgaW4gYHNjYWxlX3hfZGlzY3JldGUoZ3VpZGUgPSBndWlkZV9heGlzKG4uZG9kZ2UgPSAyKSlgIAoKYGBge3J9CiMgeW91ciBjb2RlIGhlcmUKCgpgYGAKCgojIEZpbmFsIHRhc2sgLSBQbGVhc2UgZ2l2ZSB1cyB5b3VyIGluZGl2aWR1YWwgZmVlZGJhY2shCgpXZSB3b3VsZCBiZSBncmF0ZWZ1bCBpZiB5b3UgY291bGQgdGFrZSBhIG1pbnV0ZSBiZWZvcmUgdGhlIGVuZCBvZiB0aGUgd29ya3Nob3Agc28gd2UgY2FuIGdldCB5b3VyIGZlZWRiYWNrIQoKPGh0dHBzOi8vbHNlLmV1LnF1YWx0cmljcy5jb20vamZlL2Zvcm0vU1ZfNmVTck9WV3VpdDI4cWNTP2NvdXJzZW5hbWU9UiVEYXRhJVZpc3VhbGlzYXRpb24lMjolQm94LCVoaXN0b2dyYW0sJWFuZCVsaW5lJXBsb3RzJnRvcGljPVImcHJvZz1EUyZ2ZXJzaW9uPTIzLTI0Jmxpbms9aHR0cHM6Ly9sc2VjbG91ZC5zaGFyZXBvaW50LmNvbS86Zjovcy9URUFNX0FQRC1EU0wtRGlnaXRhbC1Ta2lsbHMtVHJhaW5lcnMvRWxZLUlmd3hnbmRMdXdfUkJMUGpSSDRCcnJBazhrUGk1S1BHWEhaYUNad2tzQT9lPW9ldk5yQT4KClRoZSBzb2x1dGlvbnMgd2UgYmUgYXZhaWxhYmxlIGZyb20gYSBsaW5rIGF0IHRoZSBlbmQgb2YgdGhlIHN1cnZleS4KCiMgSW5kaXZpZHVhbCBjb2RpbmcgY2hhbGxlbmdlCgpGb3IgdGhlIGNvZGluZyBjaGFsbGVuZ2Ugd2Ugd2lsbCBsb29rIGF0IG90aGVyIHRoaW5ncyB5b3UgY2FuIGRvIHdpdGggZ2dwbG90MiBzdWNoIGFzIG1ha2luZyBhcnR3b3JrISBUaGlzIGlzIGtub3duIGFzIGdlbmVyYXRpdmUgYXJ0LCB3aGljaCBpcyBwcm9kdWNlZCBlaXRoZXIgaW4gcGFydCBvciBjb21wbGV0ZWx5IGJ5IGF1dG9tYXRlZCBwcm9jZXNzZXMuIAoKR2VuZXJhdGl2ZSBhcnQgaXMgYSBjb21wbGV4IHRvcGljLCBidXQgc29tZSBvZiB0aGUgaWRlYXMgYW5kIHN0eWxlcyBjYW4gYmUgZG9uZSB1c2luZyB0aGUgYVJ0c3kgcGFja2FnZSwgPGh0dHBzOi8va29lbmRlcmtzLmdpdGh1Yi5pby9hUnRzeS8+LCB3aGljaCBtYWtlcyBnZW5lcmF0aXZlIGFydCBtb3JlIGFjY2Vzc2libGUuIAoKRmlyc3QsIHlvdSB3aWxsIG5lZWQgdG8gaW5zdGFsbCB0aGUgYVJ0c3kgcGFja2FnZS4gCmBgYHtyIGV2YWw9RkFMU0V9CiMgaW5zdGFsbCBhUnRzeQppbnN0YWxsLnBhY2thZ2VzKCJhUnRzeSIpCmBgYAoKVGhlbiB5b3Ugd2lsbCBuZWVkIHRvIGxvYWQgaXQhIApgYGB7cn0KIyBsb2FkIGFSdHN5CmxpYnJhcnkoYVJ0c3kpCmBgYAoKV2hlbiBtYWtpbmcgZ2VuZXJhdGl2ZSBhcnQgaXQgaXMgYSBnb29kIGlkZWEgdG8gbWFrZSBpdCByZXByb2R1Y2libGUgYXMgd2UgdGhlcmUgaXMgYSBsb3Qgb2YgcmFuZG9taXNhdGlvbiBpbnZvbHZlZC4gV2hlbiByYW5kb21pc2luZyBpbiBSIHlvdSBuZWVkIHRvICpzZXQgYSBzZWVkKiwgd2hpY2ggaW4gc2ltcGxlIHRlcm1zIG1lYW5zIHdlIHJlcHJvZHVjZSBvdXIgcmVzdWx0cyB1c2luZyB0aGUgc2FtZSBzZWVkLiBXZSB1c2UgdGhlIGBzZXQuc2VlZCgpYCBmdW5jdGlvbiBhbmQgYWRkIGluIGFueSBudW1iZXIuIFRoZSBudW1iZXIgaXMgb3VyIHNlZWQuIElmIHdlIGdhdmUgc29tZW9uZSBlbHNlIG91ciBjb2RlIGFuZCBvdXIgc2VlZCB0aGV5IHdvdWxkIGJlIGFibGUgdG8gcmVwcm9kdWNlIG9yIHJlc3VsdHMuCgpXZSd2ZSBnaXZlbiBzb21lIGV4YW1wbGVzIGJlbG93IG9uIG1ha2luZyBhIHN0cmlwZWQgYXJ0d29yayBhbmQgZmxvdyBmaWVsZHMuIFJ1biB0aGUgY29kZSBjaHVuayBiZWxvdywgdGhlbiB0cnkgY2hhbmdpbmcgdGhlIHNlZWQgdG8gc2VlIGhvdyB0aGUgcmVzdWx0cyBjaGFuZ2Ugd2hlbiB5b3UgcnVuIGl0IGFnYWluISAKCk5vdGU6IHRoZXNlIHdpbGwgdGFrZSBhIGZldyBtb21lbnRzIHRvIHJ1biEgCmBgYHtyfQojIHNldCB0aGUgc2VlZCB0byAxCnNldC5zZWVkKDEpCgojIG1ha2UgYSBjb2xvdXIgcGFsZXR0ZSBmcm9tIHJjb2xvcmJyZXdlcgpzZXQxIDwtIGJyZXdlci5wYWwobiA9IDksIG5hbWUgPSAiU2V0MSIpCnBhc3RlbDEgPC0gYnJld2VyLnBhbChuID0gOSwgbmFtZSA9ICJQYXN0ZWwxIikKcGFpcmVkIDwtIGJyZXdlci5wYWwobiA9IDEyLCBuYW1lID0gIlBhaXJlZCIpCgojIHRlc3Qgb3V0IGRpZmZlcmVudCBwYXJhbWV0ZXJzIGZvciBzdHJpcGVzCmNhbnZhc19zdHJpcGVzKHBhaXJlZCwgbiA9IDgwMCwgSCA9IDUsIGJ1cm5pbiA9IDUpCgpjYW52YXNfc3RyaXBlcyhwYXN0ZWwxLCBuID0gNTAwLCBIID0gMTUsIGJ1cm5pbiA9IDIpCgojIFRlc3Qgb3V0IGRpZmZlcmVudCBwYXJhbWV0ZXJzIGZvciBmbG93IGZpZWxkcwpjYW52YXNfZmxvdyhzZXQxLCBiYWNrZ3JvdW5kID0gIiNmYWZhZmEiLCBsaW5lcyA9IDgwMCwgbHdkID0gMC4zMCwKICAgICAgICAgICAgaXRlcmF0aW9ucyA9IDgwLCBzdGVwbWF4ID0gMC4xNSkKCnBhc3RlbF9mbG93IDwtIGNhbnZhc19mbG93KHBhc3RlbDEsIGJhY2tncm91bmQgPSAiYmxhY2siLCBsaW5lcyA9IDIwMDAsIGx3ZCA9IDAuMTUsCiAgICAgICAgICAgIGl0ZXJhdGlvbnMgPSAzMCwgc3RlcG1heCA9IDAuMTApCgpwYXN0ZWxfZmxvdwoKIyBzYXZlIHBhc3RlbF9mbG93CnNhdmVDYW52YXMocGFzdGVsX2Zsb3csICJwYXN0ZWxfZmxvdy5wbmciKQpgYGAKCkhhdmUgYSBnbyB5b3Vyc2VsZiBhdCBtYWtpbmcgc29tZSBnZW5lcmF0aXZlIGFydCBpbiBSISBUcnkgb3V0IHRoZSBmb2xsb3dpbmcgZnVuY3Rpb25zIGZyb20gYVJ0c3ksIGNoYW5naW5nIHRoZSBwYXJhbWV0ZXJzIHRvIGFkanVzdCB0aGUgdmlzdWFsaXNhdGlvbi4gCgotIGBjYW52YXNfZmxvdygpYCA8aHR0cHM6Ly9rb2VuZGVya3MuZ2l0aHViLmlvL2FSdHN5L3JlZmVyZW5jZS9jYW52YXNfZmxvdy5odG1sPgotIGBjYW52YXNfc3RyaXBlcygpYCA8aHR0cHM6Ly9rb2VuZGVya3MuZ2l0aHViLmlvL2FSdHN5L3JlZmVyZW5jZS9jYW52YXNfc3RyaXBlcy5odG1sPgotIGBjYW52YXNfd2F0ZXJjb2xvcnMoKWAgPGh0dHBzOi8va29lbmRlcmtzLmdpdGh1Yi5pby9hUnRzeS9yZWZlcmVuY2UvY2FudmFzX3dhdGVyY29sb3JzLmh0bWw+CgpEb24ndCBmb3JnZXQgdG8gc2F2ZSBhbnkgb2YgeW91ciBhcnR3b3JrIHlvdSBsaWtlIHVzaW5nIHRoZSBgc2F2ZUNhbnZhcygpYCBmdW5jdGlvbi4gCmBgYHtyfQpzZXQuc2VlZCgxKQoKIyB5b3VyIGNvZGUgaGVyZQoKCmBgYAoKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZWNvbW1lbmVkIHJlc291cmNlcyB0byBjb250aW51ZSB5b3VyIGRhdGEgdmlzdWFsaWF0aW9uIGxlYXJuaW5nCgpUaGUgZ2dwbG90MiBib29rIGlzIGFuIGV4Y2VsbGVudCByZXNvdXJjZSB3aXRoIGxvdHMgb2YgZXhhbXBsZXMgYW5kIGV4ZXJjaXNlcyB0byBoYXZlIGEgZ28gYXQgPGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy8+LiAKCkNlZHJpYyBTY2hlcmVyIHdyaXRlcyBibG9ncyBhbmQgdHV0b3JpYWxzIG9uIGdncGxvdDIgb24gaGlzIHdlYnNpdGUuIFNvbWUgb2YgaGlzIGNvbnRlbnQgaXMgcmVhbGx5IGdyZWF0IGFuZCB3b3J0aCBsb29raW5nIHRocm91Z2guIEJlbG93IGFyZSB0d28gb2YgaGlzIHR1dG9yaWFscyB0byBnZXQgeW91IHN0YXJ0ZWQ6CgotIDxodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA4LzA1L2EtZ2dwbG90Mi10dXRvcmlhbC1mb3ItYmVhdXRpZnVsLXBsb3R0aW5nLWluLXIvPgotIDxodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA1LzE3L3RoZS1ldm9sdXRpb24tb2YtYS1nZ3Bsb3QtZXAuLTEvPgoKR2Vvcmdpb3MgS2FyYW1hbmlzIGlzIGEgZGF0YSB2aXN1YWxpc2F0aW9uIGRlc2lnbmVyIGFuZCBtYWtlcyBzb21lIGFtYXppbmcgdmlzdWFsaXNhdGlvbnMgdXNpbmcgUiEgSXQncyB3b3J0aCBicm93c2luZyBoaXMgd2Vic2l0ZSBmb3IgaW5zcGlyYXRpb24gPGh0dHBzOi8va2FyYW1hbi5pcy8+IG9yIGZvbGxvd2luZyBoaW0gb24gdHdpdHRlciA8aHR0cHM6Ly90d2l0dGVyLmNvbS9nZW9rYXJhbWFuaXM+LgoKRm9yIGlkZWFzIGFib3V0IHdoYXQgdG8gZG8gd2l0aCB5b3VyIGRhdGEgaGF2ZSBhIGxvb2sgYXQgdGhlIFIgZ3JhcGggZ2FsbGVyeSA8aHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS8+LiAKClJlY29tbWVuZGVkIHdhdGNoaW5nIGZvciBvcGluaW9uYXRlZCAoYW5kIGFjY3VyYXRlKSB2aWV3cyBvbiBkYXRhIHZpc3VhbGlzYXRpb24KCiogVmlkZW86IDxodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvcnN0dWRpb2NvbmYtMjAyMC90aGUtZ2xhbW91ci1vZi1ncmFwaGljcy8+CiogU2xpZGVzOiA8aHR0cHM6Ly93d3cud2lsbGlhbXJjaGFzZS5jb20vc2xpZGVzL2Fzc2V0cy9wbGF5ZXIvS2V5bm90ZURIVE1MUGxheWVyLmh0bWwjMD4KCg==